﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using JetBrains.Annotations;
using Kaleida.ServiceMonitor.Model;
using System.Linq;

namespace Kaleida.ServiceMonitor.UI
{
    public partial class ScriptWorkspaceControl : UserControl
    {
        private ScriptWorkspace workspace = new ScriptWorkspace();
        private bool suppressSelectedIndexChangedEvent;
        private string scriptDirectoryPath;

        public ScriptWorkspaceControl()
        {
            InitializeComponent();
            cbScripts.SelectedIndexChanged += cbScripts_SelectedIndexChanged;
            cbScripts.DisplayMember = "NameAndDirtyStatus";

        	scriptEditor.SaveRequested += (s, e) => SaveCurrentScript();
        }

        [NotNull]
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ScriptWorkspace Workspace
        {
            get { return workspace; }
            set
            {
                if (value == null) throw new ArgumentNullException("value");
                workspace = value;
                workspace.CurrentScriptChanged += CurrentScriptChanged;
                workspace.CurrentScriptContentChanged += CurrentScriptContentChanged;
                workspace.LoadedScriptListChanged += LoadedScriptListChangedHandler;
            }
        }

        public string ScriptDirectoryPath
        {
            get { return scriptDirectoryPath; }
            set { scriptDirectoryPath = value; }
        }

        public void CurrentScriptChanged(object sender, EventArgs e)
        {
            var currentScript = workspace.CurrentScript;

            var selectedIndex = workspace.LoadedScripts.IndexOf(currentScript);
            cbScripts.SelectedIndex = selectedIndex;
            scriptEditor.Script = currentScript;

            UpdateSaveButtonState();
        }

        public void CurrentScriptContentChanged(object sender, EventArgs e)
        {
            UpdateSaveButtonState();

            cbScripts.Text = workspace.CurrentScript.GetNameAndEditState();
            RefreshComboBoxInPlace();
        }

        public void LoadedScriptListChangedHandler(object sender, EventArgs e)
        {
            ReloadComboBoxAndReselect();
        }

        private void ReloadComboBoxAndReselect()
        {
            var selectedScriptName = workspace.CurrentScript.Name;

            suppressSelectedIndexChangedEvent = true;
            cbScripts.Items.Clear();
            foreach (var script in workspace.LoadedScripts)
            {
                cbScripts.Items.Add(script.GetNameAndEditState());
            }
            suppressSelectedIndexChangedEvent = false;

            TrySelectScript(selectedScriptName);
        }

        private void RefreshComboBoxInPlace()
        {
            suppressSelectedIndexChangedEvent = true;
            for (int i = 0; i < workspace.LoadedScripts.Count; i++)
            {
                cbScripts.Items[i] = workspace.LoadedScripts[i].GetNameAndEditState();
            }
            suppressSelectedIndexChangedEvent = false;
        }

        public void TrySelectScript(string scriptName)
        {
            var matching = workspace.LoadedScripts.FirstOrDefault(i => string.Equals(i.Name, scriptName, StringComparison.OrdinalIgnoreCase));
            workspace.CurrentScript = matching ?? workspace.LoadedScripts.FirstOrDefault() ?? new NullScriptSource();
        }

        private void cbScripts_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (suppressSelectedIndexChangedEvent) return;

            var isValidScriptIndex = cbScripts.SelectedIndex >= 0 && cbScripts.SelectedIndex < workspace.LoadedScripts.Count();
            workspace.CurrentScript = isValidScriptIndex ? workspace.LoadedScripts[cbScripts.SelectedIndex] : new NullScriptSource();

            scriptEditor.Script = workspace.CurrentScript;
            
            UpdateSaveButtonState();
            scriptEditor.Focus();
        }

        private void btnNewScript_Click(object sender, EventArgs e)
        {
            var dialog = new NewScriptDialog();
            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                var newPath = Path.Combine(scriptDirectoryPath, dialog.ScriptName + ".mscr");
                if (File.Exists(newPath))
                {
                    MessageBox.Show(string.Format("A script already exists with the name '{0}'", dialog.ScriptName), "Cannot create new script", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }

                workspace.NewScript(newPath);
                
                scriptEditor.Focus();
            }
        }

        private void btnReload_Click(object sender, EventArgs e)
        {
            bool cancelled = AllowUserToSaveDirty();
            if (!cancelled)
            {
                var selectedScriptName = workspace.CurrentScript.Name;
                workspace.UnloadAllScripts();
                LoadScriptsFromScriptsDirectory();
                TrySelectScript(selectedScriptName);
            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            SaveCurrentScript();
        }

        public void LoadScriptsFromDisk()
        {
            LoadScriptsFromScriptsDirectory();
            UpdateSaveButtonState();
        }

        private void LoadScriptsFromScriptsDirectory()
        {
            if (!Directory.Exists(scriptDirectoryPath))
                throw new InvalidOperationException(string.Format("The required Scripts directory '{0}' does not exist", scriptDirectoryPath));

            var scriptPaths = Directory.GetFiles(scriptDirectoryPath, "*.mscr", SearchOption.AllDirectories);
            
            foreach(var scriptPath in scriptPaths)
            {
                workspace.LoadScript(scriptPath);
            }
        }

        private void UpdateSaveButtonState()
        {
            btnSave.Enabled = !(workspace.CurrentScript is NullScriptSource) && workspace.CurrentScript.IsDirty;
        }

        private void SaveCurrentScript()
        {
            workspace.SaveCurrentScript();
            UpdateSaveButtonState();
            RefreshComboBoxInPlace();
        }

        public bool AllowUserToSaveDirty()
        {
            bool cancelled = false;
            var unsavedScripts = workspace.LoadedScripts.Where(i => i.IsDirty);

            foreach (var unsavedScript in unsavedScripts)
            {
                workspace.CurrentScript = unsavedScript;

                var result = MessageBox.Show(string.Format("Save changes to '{0}'?", unsavedScript.Name), "Unsaved script", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

                if (result == DialogResult.Cancel)
                {
                    cancelled = true;
                    break;
                }

                if (result == DialogResult.Yes)
                {
                    SaveCurrentScript();
                }
            }
            return cancelled;
        }

        private void ScriptWorkspaceControl_Resize(object sender, EventArgs e)
        {
            bool hasConstrainedHeight = Height < 100;
            bool hasConstrainedWidth = Width < 360 ;

            lblAvailableScripts.Visible = !hasConstrainedWidth && !hasConstrainedHeight;

            cbScripts.Visible = !hasConstrainedHeight;
            scriptEditor.Location = hasConstrainedHeight ? new Point(3, 8) : new Point(3, 29);
            scriptEditor.Height = hasConstrainedHeight ? Height - 10 : Height - 29;

            cbScripts.Location = hasConstrainedWidth ? new Point(8, 2) : new Point(97, 2);
            cbScripts.Width = hasConstrainedWidth ? Width - 16 : Width - 274;

            btnNewScript.Visible = !hasConstrainedWidth && !hasConstrainedHeight;
            btnReload.Visible = !hasConstrainedWidth && !hasConstrainedHeight;
            btnSave.Visible = !hasConstrainedWidth && !hasConstrainedHeight;
        }

    }
}
